Skip to content

Add PUT /users/:id (update a user) - #130

Open
tvk777 wants to merge 4 commits into
mate-academy:mainfrom
tvk777:develop
Open

Add PUT /users/:id (update a user)#130
tvk777 wants to merge 4 commits into
mate-academy:mainfrom
tvk777:develop

Conversation

@tvk777

@tvk777 tvk777 commented Sep 4, 2026

Copy link
Copy Markdown

What

Adds the missing "update a user" endpoint to the users resource: PUT /users/:id.

  • db/store.js — new updateUser(id, { name, email }) helper alongside the existing
    getUserById / createUser. It reuses getUserById for the lookup, replaces the fields
    in place, and returns undefined for an unknown id so the store stays free of HTTP concerns.
  • routes/users.js — the PUT /users/:id handler. Full-replacement semantics: both
    name and email are required. All data access goes through the store, matching the
    other handlers.
  • NOTES.md — the decisions behind the change (plan, model choice, commit split, review).

Why

The users resource supported list, get-by-id and create, but there was no way to update a
user. tests/update-user.test.js already specified the contract and was red; this makes it
green without touching the test file.

Behavior

Request Response
Valid update of an existing user 200 + the updated user
Unknown id 404 {"error":"User not found"}
Missing / empty / non-string name or email 400 {"error":"name and email are required"}
Non-numeric id 400 {"error":"id must be a number"}

Two deliberate choices worth a reviewer's attention:

  • Validation runs before the store lookup, so a malformed body on an unknown id is a
    400 and never a partially applied write. The tests don't exercise that conflicting case,
    so the ordering is a decision rather than something the suite pins down.
  • No email-format regex. POST /users doesn't validate format either, and having create
    and update disagree about what a valid email is would be worse than having neither check.

What a reviewer should test

npm install && npm run lint && npm test    # 9/9 green, lint clean

Then against a running server (npm run dev):

  • curl -X PUT localhost:3000/users/1 -H 'content-type: application/json' -d '{"name":"Ada L.","email":"ada@new.com"}' → 200; GET /users/1 and GET /users both show the new values.
  • PUT /users/9999 with a valid body → 404, not a crash.
  • PUT /users/1 -d '{"name":"only a name"}' → 400.
  • Edge cases: whitespace-only name, {"name": 42}, and an absent body → 400 rather than a 500.
  • PUT /users/abc → 400.
  • PUT /users/0x2 → 400 (see below).

Note on the id guard

A self-review caught that Number(req.params.id) is laxer than its own error message:
0x2, +2, 2e0, 2.0 and a URL-encoded %202 all coerce to 2, so PUT /users/0x2
returned a 200 and overwrote user 2. The provided tests wouldn't catch it — they only try a
well-formed id and a fully non-numeric one. The last commit matches the raw param against
/^\d+$/ before converting.

Out of scope

POST /users still uses a bare truthiness check, so it accepts {"name": 42} and
whitespace-only names — create and update now disagree slightly on field validity. That's
pre-existing code this change doesn't touch; happy to tighten it in a follow-up.

🤖 Generated with Claude Code

tvk777 and others added 4 commits September 4, 2026 17:34
Adds an updateUser(id, fields) function to the in-memory store, following
the existing getUserById/createUser pattern. It reuses getUserById for the
lookup, replaces name and email in place, and returns undefined for an
unknown id so route handlers stay in charge of the HTTP response.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds a full-replacement update endpoint to the users resource. It validates
that name and email are present, non-empty strings before touching the store
(400 otherwise), rejects a non-numeric id with a 400, and returns 404 when no
user has that id instead of crashing. Data access goes through the store's
new updateUser helper, matching the other handlers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Records the decisions behind the update-user change: the plan and the two
questions settled before approving it, the model choice, why the commits
were split store-then-route, and what the self-review caught.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Number() coerces "0x2", "+2", "2e0", "2.0" and a URL-encoded " 2" all to 2,
so those forms bypassed the id guard and updated user 2 with a 200 despite the
handler claiming to require a number. Match the raw param against /^\d+$/
before converting, and note the finding in NOTES.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant